import React, {Component} from 'react';
import './App.css';
import PropTypes from "prop-types"
import {sortBy} from 'lodash';
import classNames from 'classnames';
const DEFAULT_QUERY = 'redux';
const DEFAULT_HPP = '10';
const PATH_BASE = 'https://hn.algolia.com/api/v1';
const PATH_SEARCH = '/search';
const PARAM_SEARCH = 'query=';
const PARAM_PAGE = 'page=';
const PARAM_HPP = 'hitsPerPage=';
const Loading = () => <div>loading....</div>
const SORTS = {
    NONE: list => list,
    TITLE: list => sortBy(list, 'title'),
    AUTHOR: list => sortBy(list, 'author'),
    COMMENTS: list => sortBy(list, 'num_comments').reverse(),
    POINTS: list => sortBy(list, 'points').reverse(),
};

class Button extends Component {
    constructor(props) {
        super(props)
    }

    render() {
        const {className = "", onClick, children} = this.props;
        return (
            <button type="button" className={className}
                    onClick={onClick}
            >
                {children}
            </button>
        )
    }

}

Button.propTypes = {
    onClick: PropTypes.func.isRequired,
    className: PropTypes.string,
    children: PropTypes.node.isRequired,
};

class Search extends Component {
    constructor(props) {
        super(props)
    }

    componentDidMount() {
        if (this.input) {
            this.input.focus()
        }
    }

    render() {
        const {value, onSearchSubmit, onChange, children} = this.props
        return (
            <div>
                <form>
                    {children}
                    <input type="text"
                           onChange={onChange}
                           value={value}
                           ref={(node) => {
                               this.input = node;
                           }}
                    />
                    <Button onClick={onSearchSubmit}>搜索</Button>
                </form>
            </div>
        )
    }
}

class Table extends Component {
    constructor(props) {
        super(props)
        this.state = {
            sortKey: 'NONE',
            isSortReverse: false,
        }
        this.onSort = this.onSort.bind(this);
    }
    onSort(sortKey) {
        const isSortReverse = this.state.sortKey === sortKey && !this.state.isSortReverse;
        this.setState({ sortKey, isSortReverse });
    }
    render() {
        const largeColumn = {
            width: '40%',
        };
        const midColumn = {
            width: '30%',
        };
        const smallColumn = {
            width: '10%',
        };
        const {list, onDismiss} = this.props;
        const {
            sortKey,
            isSortReverse,
        } = this.state;
        let dataArr = SORTS[sortKey](list);
        dataArr = isSortReverse ?dataArr.reverse() : dataArr;
        let onSort = this.onSort;
        return (
            <div className="table">
                <div className="table-header">
                    <span style={{width: '40%'}}>
                        <Sort
                            sortKey={'TITLE'}
                            onSort={onSort}
                            activeSortKey={sortKey}
                        >
                        Title
                        </Sort>
                    </span>
                    <span style={{width: '30%'}}>
                        <Sort
                            sortKey={'AUTHOR'}
                            onSort={onSort}
                            activeSortKey={sortKey}
                        >Author
                        </Sort>
                    </span>
                    <span style={{width: '10%'}}>
                        <Sort
                            sortKey={'COMMENTS'}
                            onSort={onSort}
                            activeSortKey={sortKey}
                        >
                        Comments
                        </Sort>
                    </span>
                    <span style={{width: '10%'}}>
                        <Sort
                            sortKey={'POINTS'}
                            onSort={onSort}
                            activeSortKey={sortKey}
                        >
                        Points
                        </Sort>
                    </span>
                    <span style={{width: '10%'}}>
                        Archive
                    </span>
                </div>
                {dataArr.map((item) =>
                    <div key={item.objectID} className="table-row">
                            <span style={largeColumn}>
                                <a href={item.url}>{item.title}</a>
                            </span>
                        <span style={midColumn}>{item.author}</span>
                        <span style={smallColumn}>{item.num_comments}</span>
                        <span style={smallColumn}>{item.points}</span>
                        <span style={smallColumn}>
                                <Button onClick={() => onDismiss(item.objectID)}
                                        className="button-inline">
                                    Dismiss
                                </Button>
                            </span>
                    </div>
                )}
            </div>
        )
    }
}

const Sort = ({sortKey, onSort, children,activeSortKey,}) =>{
    const sortClass = classNames(
        "button-line",
        {"button-active":sortKey === activeSortKey}
    )
    return(
        <Button onClick={() => onSort(sortKey)} className={sortClass}>
            {children}
        </Button>
    )

}


Table.propTypes = {
    list: PropTypes.array.isRequired,
    onDismiss: PropTypes.func.isRequired,
};
const updateSearchTopStoriesState =(hits, page)=>{
    return (prevState)=>{
        const {searchKey, results} = prevState;
        const oldHits = results && results[searchKey]
            ? results[searchKey].hits
            : [];
        const updatedHits = [
            ...oldHits,
            ...hits
        ];
        return{
            results: {
                ...results,
                [searchKey]: {hits: updatedHits, page}
            },
            isLoading: false
        }

    }
}
class App extends Component {
    constructor(props) {
        super(props);
        this.state = {
            results: null,
            searchKey: '',
            searchItem: DEFAULT_QUERY,
            error: null,
            isLoading: false,
        };
        this.needsToSearchTopStories = this.needsToSearchTopStories.bind(this);
        this.onSearchChange = this.onSearchChange.bind(this);
        this.onDismiss = this.onDismiss.bind(this);
        this.setSearchTopStories = this.setSearchTopStories.bind(this);
        this.fetchSearchTopStories = this.fetchSearchTopStories.bind(this);
        this.onSearchSubmit = this.onSearchSubmit.bind(this);
    }


    onSearchSubmit() {
        const {searchItem} = this.state;
        this.setState({searchKey: searchItem});
        if (this.needsToSearchTopStories(searchItem)) {
            this.fetchSearchTopStories(searchItem);
        }
    }

    setSearchTopStories(result) {
        const {hits, page} = result;

        this.setState(updateSearchTopStoriesState(hits, page));
    }

    needsToSearchTopStories(searchTerm) {
        return !this.state.results[searchTerm];
    }

    fetchSearchTopStories(searchTerm, page = 0) {
        this.setState({isLoading: true});
        fetch(`${PATH_BASE}${PATH_SEARCH}?${PARAM_SEARCH}${searchTerm}&${PARAM_PAGE}${page}&${PARAM_HPP}${DEFAULT_HPP}`)
            .then(response => response.json())
            .then(result => this.setSearchTopStories(result))
            .catch(e => this.setState({error: e}));
    }

    onDismiss(id, e) {
        const {searchKey, results} = this.state;
        const {hits, page} = results[searchKey];
        const isNotId = item => item.objectID !== id;
        const updatedHits = hits.filter(isNotId);
        this.setState({
            results: {
                ...results,
                [searchKey]: {hits: updatedHits, page}
            }
        });
    }

    onSearchChange(e) {
        this.setState({
            searchItem: e.target.value
        })
    }

    componentDidMount() {
        const {searchItem} = this.state;
        this.setState({searchKey: searchItem});
        this.fetchSearchTopStories(searchItem)
    }

    render() {
        const {
            searchItem,
            results,
            searchKey,
            error,
            isLoading,
        } = this.state;
        if (error) {
            return <div>error</div>
        }
        const page = (
            results &&
            results[searchKey] &&
            results[searchKey].page
        ) || 0;
        const list = (
            results &&
            results[searchKey] &&
            results[searchKey].hits
        ) || [];

        return (
            <div className="App">
                <div className="interactions">
                    <Search value={searchItem}
                            onChange={this.onSearchChange}
                            onSearchSubmit={this.onSearchSubmit}
                    >
                        Search
                    </Search>
                </div>
                {list ? <Table
                    list={list}
                    onSort={this.onSort}
                    onDismiss={this.onDismiss}
                ></Table> : null}
                <div className="interactions">
                    <ButtonWithLoading
                        isLoading={isLoading}
                        onClick={() => this.fetchSearchTopStories(searchKey, page + 1)}>
                        More
                    </ButtonWithLoading>

                </div>
            </div>
        );
    }
}

const withLoading = (Component) => ({isLoading, ...rest}) =>
    isLoading
        ? <Loading/>
        : <Component {...rest} />

const ButtonWithLoading = withLoading(Button);

export default App;
